﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Rage
{
    public static class StatConversion
    {   // Class only works for Level 80 Characters (Level 85 if you are in the RAGE4 projects)
        // Numbers reverse engineered by Whitetooth (hotdogee [at] gmail [dot] com)

        #region Character Constants
        public const int DEFAULTPLAYERLEVEL = 85;

        /*  Here are the values data-mined from build 12942 and imported into SimulationCraft:

            Base Chance to Melee Crit%: 2.9219999909401%
            Base Chance to Spell Crit%: 2.2010000422597%

            Spell Scaling Multiplier: 1004.489990234375000 (level 80 value is 900.120300292968750. i.e. Level 85 base damage of abilities is 1004.49/900.12 or *1.12 more than at level 80 and so is the damage range.)
            Agi to +Melee Crit%: 0.0030780898669%
            Int to +Spell Crit%: 0.0015410500055%
            Int/Spirit regen coefficient: 0.003345000091940

            Ratings:

            Dodge: 176.718994140625000
            Parry: 176.718994140625000
            Shield: 88.359397888183594
            Melee Hit: 120.109001159667969
            Ranged Hit: 120.109001159667969
            Spell Hit: 102.445999145507812
            Melee Crit: 179.279998779296875
            Ranged Crit: 179.279998779296875
            Spell Crit: 179.279998779296875
            Melee Haste: 128.057006835937500
            Ranged Haste: 128.057006835937500
            Spell Haste: 128.057006835937500
            Expertise: 30.027200698852539
            Mastery: 179.279998779296875

            The following is from in-game testing:
            Chance to Dodge Base%: 1.67%
            
         */

        // These are set based on the values above
        //public const float RATING_PER_DODGE         = 17671.8994140625000f;
        //public const float RATING_PER_PARRY         = 17671.8994140625000f;
        //public const float RATING_PER_BLOCK         =  8835.9397888183594f;
        //public const float RATING_PER_PHYSICALHIT   = 12010.9001159667969f;
        //public const float RATING_PER_SPELLHIT      = 10244.5999145507812f;
        //public const float RATING_PER_PHYSICALCRIT  = 17927.9998779296875f; 
        //public const float RATING_PER_SPELLCRIT     = 17927.9998779296875f; 
        //public const float RATING_PER_PHYSICALHASTE = 12805.7006835937500f; 
        //public const float RATING_PER_SPELLHASTE    = 12805.7006835937500f; 
        //public const float RATING_PER_EXPERTISE     =  30.027200698852539f; // Not a Perc, so decimal over
        //public const float RATING_PER_MASTERY       = 179.279998779296875f; // Not a Perc, so decimal over
        // These shouldn't be changing
        //public const float RATING_PER_HEALTH        = 14.00f; //14 Health per 1 STA;
        //public const float RATING_PER_MANA          = 15.00f; //15 Mana per 1 INT;
        //public const float BLOCKVALUE_PER_STR       =  2.00f;
        // These have not been provided Cata values yet, some could be removed as no longer valid
        //public const float LEVEL_85_COMBATRATING_MODIFIER      = 3.2789987789987789987789987789988f;
        //public const float RATING_PER_RESILIENCE =      9520.6611570247933884297520661157f;
        //public const float RATING_PER_DODGEPARRYREDUC          = 0.0025f; //4 Exp per 1% Dodge/Parry Reduction;
        //public const float LEVEL_AVOIDANCE_MULTIPLIER          = 0.20f;

        // Attack Table for players attacking mobs                                           50       50        50      50
        public static readonly float[] BASIC_MISS_CHANCE_CAP                = new float[] { 0.1000f, 0.1000f, 0.1000f, 0.1000f }; // 10% (space between  90% and 100% Accuracy, more accuracy lowers target defense)
        public static readonly float[] FORCE_MISS_CHANCE_CAP                = new float[] { 0.0000f, 0.0000f, 0.0000f, 0.0000f }; // 00% (space between 100% and 100% Accuracy, more accuracy lowers target defense)
        public static readonly float[] BOTH_DODGE_CHANCE_CAP              = new float[] { 0.0400f, 0.0400f, 0.0400f, 0.0400f }; // Half 8% base defense = 4%
        public static readonly float[] Both_PARRY_CHANCE_CAP              = new float[] { 0.0400f, 0.0400f, 0.0400f, 0.0400f }; // Half 8% base defense = 4%
        public static readonly float[] BOTH_SHIELD_CHANCE_CAP             = new float[] { 1.0000f, 1.0000f, 1.0000f, 1.0000f }; // 100% chance to shield (will never hit due to DR)

        /// <summary>
        /// You need to *add* this to your current crit value as it's a negative number.
        /// <para>[85: 0, 86: -0.006, 87: -0.012, 88: -0.048]</para>
        /// </summary>
        public static readonly float[] NPC_LEVEL_CRIT_MOD                   = new float[] { -0.0000f, -0.0000f, -0.0000f, -0.0000f }; //  -4.8%

        // http://elitistjerks.com/f75/t110187-cataclysm_mage_simulators_formulators/p4/#post1834778
        // Level+3 has now been confirmed as being -1.8% (-0.0180)
        /// <summary>
        /// You need to *add* this to your current crit value as it's a negative number.
        /// <para>[85: 0, 86: -0.002625, 87: -0.00525, 88: -0.0180]</para>
        /// <para>Note: Level+1 and Level+2 values are just guesstimates based on trends
        /// from NPC_LEVEL_CRIT_MOD. We don't currently have solid values for these.</para>
        /// </summary>
        public static readonly float[] NPC_LEVEL_SPELL_CRIT_MOD             = new float[] { -0.0000f, -0.0000f, -0.0000f, -0.0000f }; //  -1.8%

        /// <summary>
        /// Source: http://code.google.com/p/simulationcraft/source/browse/branches/cataclysm/engine/sc_target.cpp
        /// </summary>
        public static readonly float[] NPC_ARMOR                            = new float[] { 6000f, 6000f, 6000f, 6000f };

        // Same for all classes
        public const float INT_PER_SPELLCRIT = 648.91f;
        public const float REGEN_CONSTANT = 0.003345f;

        /// <summary>
        /// Source: http://elitistjerks.com/f15/t29453-combat_ratings_level_85_cataclysm/
        /// </summary>
        public static readonly float[] AGI_PER_PHYSICALCRIT = { 0.0f, // CharacterClass starts at 1
            243.60f, //3.905f * 62.500f,  // Warrior 1
            203.08f, //3.905f * 52.083f,  // Paladin 2
            324.85f, //3.905f * 83.333f,  // Hunter 3
            324.72f, //3.905f * 83.333f,  // Rogue 4
            203.46f, //3.905f * 52.083f,  // Priest 5
            243.70f, //3.905f * 62.500f,  // Death Knight 6
            324.88f, //3.905f * 83.333f,  // Shaman 7
            199.87f, //3.905f * 51.0204f, // Mage 8
            197.08f, //3.905f * 50.505f,  // Warlock 9
              0.00f, //3.905f *  0.000f,  // Empty 10
            324.85f, //3.905f * 83.333f,  // Druid 11
        };

        /// <summary>
        /// Source: http://elitistjerks.com/f15/t29453-combat_ratings_level_85_cataclysm/
        /// </summary>
        public static readonly float[] AGI_PER_DODGE = { 0.0f, // Starts at 0
            0, // Patch 4.2 removed Agility to Dodge for Warriors //5.309f * 84.74576271f, // Warrior 1
            0, // Patch 4.2 removed Agility to Dodge for Paladins //5.309f * 59.88023952f, // Paladin 2
            439.99947200f, //5.309f * 86.20689655f, // Hunter 3
            243.51637648f, //5.309f * 47.84688995f, // Rogue 4
            304.00034048f, //5.309f * 59.88023952f, // Priest 5
            0, // Patch 4.2 removed Agility to Dodge for DKs //5.309f * 84.74576271f, // Death Knight 6
            304.00034048f, //5.309f * 59.88023952f, // Shaman 7
            300.16238785f, //5.309f * 58.82352941f, // Mage 8
            304.35470715f, //5.309f * 59.88023952f, // Warlock 9
              0.00000000f, //5.309f *  0.0f,        // Empty 10
            243.58281085f, //5.309f * 47.84688995f, // Druid 11
        };

        public static readonly float[] DR_COEFFIENT = { 0.0f, // Starts at 0
            0.9560f, // Warrior 1
            0.9560f, // Paladin 2
            0.9880f, // Hunter 3
            0.9880f, // Rogue 4
            0.9530f, // Priest 5
            0.9560f, // Death Knight 6
            0.9880f, // Shaman 7
            0.9530f, // Mage 8
            0.9530f, // Warlock 9
            0.0f,    // Empty 10
            0.9720f, // Druid 11
        };

        // This is the cap value for DODGE PERCENTAGE.
        public static readonly float[] CAP_DODGE = { 0.0f, // Starts at 0
             65.631440f, // Warrior 1
             65.631440f, // Paladin 2
            145.560408f, // Hunter 3
            145.560408f, // Rogue 4
            150.375940f, // Priest 5
             65.631440f, // Death Knight 6
            145.560408f, // Shaman 7
            150.375940f, // Mage 8
            150.375940f, // Warlock 9
              0f,        // Empty 10
            116.890707f, // Druid 11
        };

        /// <summary>
        /// This is the 1/CAP_DODGE to cut down the ammount of math going on.
        /// </summary>
        public static readonly float[] CAP_DODGE_INV = { 0.0f, // Starts at 0
            0.01523660f, // Warrior 1
            0.01523660f, // Paladin 2
            0.00687000f, // Hunter 3
            0.00687000f, // Rogue 4
            0.00665000f, // Priest 5
            0.01523660f, // Death Knight 6
            0.00687000f, // Shaman 7
            0.00665000f, // Mage 8
            0.00665000f, // Warlock 9
            0f,          // Empty 10
            0.00855500f, // Druid 11
        };

        // This is the cap value for PARRY PERCENTAGE.
        public static readonly float[] CAP_PARRY = { 0.0f, // Starts at 0
             65.631440f, // Warrior 1
             65.631440f, // Paladin 2
            145.560408f, // Hunter 3
            145.560408f, // Rogue 4
              0f,        // Priest 5
             65.631440f, // Death Knight 6
            145.560408f, // Shaman 7
              0f,        // Mage 8
              0f,        // Warlock 9
              0f,        // Empty 10
              0f,        // Druid 11
        };

        /// <summary>
        /// This is the 1/CAP_PARRY to cut down the amount of math going on.
        /// And prevent divide by 0 errors.
        /// </summary>
        public static readonly float[] CAP_PARRY_INV = { 0.0f, // Starts at 0
            0.01523660f, // Warrior 1
            0.01523660f, // Paladin 2
            0.00687000f, // Hunter 3
            0.00687000f, // Rogue 4
            0f,          // Priest 5
            0.01523660f, // Death Knight 6
            0.00687000f, // Shaman 7
            0f,          // Mage 8
            0f,          // Warlock 9
            0f,          // Empty 10
            0f,          // Druid 11
        };

        /// <summary>This is the cap value for MISS PERCENTAGE on NPC attacks against a Player</summary>
        public static readonly float[] CAP_MISSED = { 0.0f, // Starts at 0
            16f, // Warrior 1
            16f, // Paladin 2
             0f, // Hunter 3
             0f, // Rogue 4
             0f, // Priest 5
            16f, // Death Knight 6
             0f, // Shaman 7
             0f, // Mage 8
             0f, // Warlock 9
             0f, // Empty 10
             0f, // Druid 11
        };

        #endregion

        #region Functions for Plain Rating Conversions

        /// <summary>Returns a Value (1000 = 1000 extra Health)</summary>
        /// <param name="Rating">Endurance</param>
        /// <returns>A Value (1000 = 1000 extra Health)</returns>
        public static float GetHealthFromEndurance(float Rating) { return Rating * 10; }

        /// <summary>Returns a Value (1000 = 1000 extra Health)</summary>
        /// <param name="Rating">Endurance</param>
        /// <returns>A Value (1000 = 1000 extra Health)</returns>
        public static float GetHealthRegenFromEndurance(float Rating) { return Rating * 0.03f; }

        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra Dodge)
        /// </summary>
        /// <param name="Rating">Dodge Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra Dodge)</returns>
        public static float GetDefenseFromRating(float rating) { return GetDefenseFromRating(rating, 50); }
        public static float GetDefenseFromRating(float Rating, int level)
        {
            return 0.05f + (30f * (1f - (float)Math.Pow(1f - (0.01f / 0.3f), ((Rating / (float)Math.Max(level, 20f)) / 0.55f)))) / 100f;
        }

        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="mainStatAmount">Main Stat Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetCritFromMainStat(float mainStatAmount) { return GetCritFromMainStat(mainStatAmount, 50); }
        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="mainStatAmount">Main Stat Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetCritFromMainStat(float mainStatAmount, int level) {
            return (30f * (1f - (float)Math.Pow(1f - (0.01f / 0.3f), ((mainStatAmount / Math.Max(level, 20f)) / 2.50f)))) / 100f;
        }
        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="secStatAmount">Main Stat Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetCritFromSecStat(float secStatAmount) { return GetCritFromSecStat(secStatAmount, 50); }
        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="secStatAmount">Main Stat Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetCritFromSecStat(float secStatAmount, int level)
        {
            return (30f * (1f - (float)Math.Pow(1f - (0.01f / 0.3f), ((secStatAmount / Math.Max(level, 20f)) / 2.50f)))) / 100f;
        }
        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="rating">Crit Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetCritFromRating(float rating) { return GetCritFromRating(rating, 50); }
        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="rating">Crit Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetCritFromRating(float rating, int level)
        {
            return (30f * (1f - (float)Math.Pow(1f - (0.01f / 0.3f), ((rating / Math.Max(level, 20f)) / 0.45f)))) / 100f;
        }

        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="rating">Crit Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetShieldFromRating(float rating) { return GetShieldFromRating(rating, 50); }
        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="rating">Crit Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetShieldFromRating(float rating, int level)
        {
            return (50f * (1f - (float)Math.Pow(1f - (0.01f / 0.5f), ((rating / Math.Max(level, 20f)) / 0.32f)))) / 100f;
        }

        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="rating">Crit Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetAbsorbFromRating(float rating) { return GetAbsorbFromRating(rating, 50); }
        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="rating">Crit Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetAbsorbFromRating(float rating, int level)
        {
            return (50f * (1f - (float)Math.Pow(1f - (0.01f / 0.5f), ((rating / Math.Max(level, 20f)) / 0.18f)))) / 100f;
        }

        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="rating">Crit Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetCritDamageMultiplierFromSurgeRating(float rating) { return GetCritDamageMultiplierFromSurgeRating(rating, 50); }
        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra chance to Crit)
        /// </summary>
        /// <param name="rating">Crit Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra chance to Crit)</returns>
        public static float GetCritDamageMultiplierFromSurgeRating(float rating, int level)
        {
            return (30f * (1f - (float)Math.Pow(1f - (0.01f / 0.3f), ((rating / Math.Max(level, 20f)) / 0.11f)))) / 100f;
        }

        /// <summary>
        /// Returns a Percentage (0.95 = 5% faster activation time)
        /// </summary>
        /// <param name="rating">Alacrity Rating</param>
        /// <param name="alacritypercbonuses">Alacrity % Bonuses</param>
        /// <param name="level">CharacterClass</param>
        /// <returns>A Percentage (0.95 = 5% faster activation time)</returns>
        public static float GetAlacrityModifierFromRating(float rating, float alacritypercbonuses, int level) {
            return 1f - alacritypercbonuses - (30f * (1f - (float)Math.Pow(1f - (0.01f / 0.3f), ((rating / (float)Math.Max(level, 20))/0.55f))) / 100f);
        }
        public static float GetAlacrityModifierFromRating(float rating, float alacritypercbonuses) { return GetAlacrityModifierFromRating(rating, alacritypercbonuses, 50); }
        public static float GetAlacrityModifierFromRating(float rating) { return GetAlacrityModifierFromRating(rating, 0.00f, 50); }

        /// <summary>
        /// Returns a Percentage (0.05 = 5% extra Hit)
        /// </summary>
        /// <param name="Rating">Hit Rating</param>
        /// <returns>A Percentage (0.05 = 5% extra Hit)</returns>
        public static float GetBasicMhAccuracyFromRating(float rating)          { return 0.90f + GetAccuracyFromRating(rating); }
        public static float GetBasicOhAccuracyFromRating(float rating)   { return 0.57f + GetAccuracyFromRating(rating); }
        public static float GetForceMhAccuracyFromRating(float rating)        { return 1.00f + GetAccuracyFromRating(rating); }
        public static float GetForceOhAccuracyFromRating(float rating) { return 0.67f + GetAccuracyFromRating(rating); }
        public static float GetAccuracyFromRating(float rating)               { return GetAccuracyFromRating(rating, 50); }
        public static float GetAccuracyFromRating(float rating, int level) {
            return 30f * (1f - (float)Math.Pow(1f - (0.01f / 0.3f), ((rating / Math.Max(level, 20) / 0.55f)))) / 100f;
        }

        public static float ApplyMultiplier(float baseValue, float multiplier)
        {
            return (baseValue * (1f + multiplier));
        }
        public static float ApplyInverseMultiplier(float baseValue, float multiplier)
        {
            return (baseValue * (1f - multiplier));
        }
        #endregion

        #region Functions for More complex things.

        /// <summary>Originally from Bear. This should be updated once per expansion</summary>
        public const float MitigationScaler         = 10000f;//78591f;
        public const double SurvivalScalerBase      = 0.1574901d; // fourToTheNegativeFourThirds = Math.Pow(4d, -4d / 3d)
        public const double SurvivalScalerTopRight  = 0.6299605d; // topRight = Math.Pow(fourToTheNegativeFourThirds, 1d / 4d)

        /// <summary>
        /// Returns how much physical damage is reduced from Armor. (0.095 = 9.5% reduction)
        /// <para>This function used to take in ArP Rating but that was removed from the game in Cata</para>
        /// </summary>
        /// <remarks>
        /// * http://forums.worldofwarcraft.com/thread.html?topicId=16473618356&sid=1&pageNo=4 post 77.<br/>
        /// * Ghostcrawler vs theorycraft.<br/>
        /// * http://elitistjerks.com/f15/t29453-combat_ratings_level_85_cataclysm/p24/#post1841717<br/>
        /// </remarks>
        /// <param name="TargetArmor">Armor of Target</param>
        /// <param name="ArmorIgnoreDebuffs">Armor reduction on target as result of Debuffs (Sunder/Fearie Fire) These are Multiplied.</param>
        /// <param name="ArmorIgnoreBuffs">Armor reduction buffs on player (Mace Spec, Battle Stance, etc) These are Added.</param>
        /// <returns>How much physical damage is reduced from Armor. (0.095 = 9.5% reduction)</returns>
        public static float GetArmorDamageReduction(float TargetArmor, float ArmorIgnoreDebuffs, float ArmorIgnoreBuffs)
        {
            float moddedTargetArmor = TargetArmor * (1f - ArmorIgnoreDebuffs) * (1f - ArmorIgnoreBuffs);
            float retVal = moddedTargetArmor / (moddedTargetArmor + 200f * 50f + 800f);
            return retVal;
        }

        /// <summary>
        /// Returns a Percent giving Average Magical Damage Resisted (0.16 = 16% Resisted)
        /// </summary>
        /// <param name="AttackerLevel">Level of the Attacker</param>
        /// <param name="TargetLevel">Level of the Target</param>
        /// <param name="TargetResistance">Targets Resistance</param>
        /// <param name="AttackerSpellPenetration">Attackers Spell Penetration</param>
        /// <returns>A Percent giving Average Magical Damage Resisted (0.16 = 16% Resisted)</returns>
        public static float GetAverageResistance(int AttackerLevel, int TargetLevel,
            float TargetResistance, float AttackerSpellPenetration)
        {
            float ActualResistance = (float)Math.Max(0f, TargetResistance - AttackerSpellPenetration);
            return ActualResistance / (AttackerLevel * 5f + ActualResistance)
                   /*+ 0.02f * (float)Math.Max(0, TargetLevel - AttackerLevel)*/; // apparently level-based partial resists were removed in Cataclysm
        }

        /// <summary>
        /// Returns a Table giving the chance to fall within a resistance slice cutoff.
        /// The table is float[11] Table, where Table[0] is 0% resisted, and Table[10] is 100% resisted.
        /// Each Table entry gives how much chance to roll into that slice.
        /// So if Table[1] contains 0.165, that means you have a 16.5% chance to resist 10% damage.
        /// </summary>
        /// <param name="AttackerLevel">Level of the Attacker</param>
        /// <param name="TargetLevel">Level of the Target</param>
        /// <param name="TargetResistance">Targets Resistance</param>
        /// <param name="AttackerSpellPenetration">Attackers Spell Penetration</param>
        /// <returns>A Table giving the chance to fall within a resistance slice cutoff.</returns>
        public static float[] GetResistanceTable(int AttackerLevel, int TargetLevel,
            float TargetResistance, float AttackerSpellPenetration)
        {                      //   00% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
            float[] ResistTable = { 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f };
            float AverageResistance = GetAverageResistance(AttackerLevel, TargetLevel, TargetResistance, AttackerSpellPenetration);

            for (int x = -1; x < 11; x++)
            {   // Build Table
                float ResistSlice = (float)Math.Max(0f, 0.5f - 2.5f * (float)Math.Abs(0.1f * x - AverageResistance));
                if (x == -1)
                {   // Adjust 0% and 10% for "negative" resists.
                    ResistTable[0] += 2f * ResistSlice;
                    ResistTable[1] -= 1f * ResistSlice;
                }
                else
                    ResistTable[x] += ResistSlice;
            }
            return ResistTable;
        }

        /// <summary>
        /// Returns a String version of the Table giving the chance to fall within a resistance slice cutoff.
        /// Useful as part of a tooltip
        /// </summary>
        /// <param name="AttackerLevel">Level of the Attacker</param>
        /// <param name="TargetLevel">Level of the Target</param>
        /// <param name="TargetResistance">Targets Resistance</param>
        /// <param name="AttackerSpellPenetration">Attackers Spell Penetration</param>
        /// <returns>A string version of a Table giving the chance to fall within a resistance slice cutoff.</returns>
        public static string GetResistanceTableString(int AttackerLevel, int TargetLevel,
            float TargetResistance, float AttackerSpellPenetration)
        {
            int count;
            string tipResist = string.Empty;
            tipResist = Math.Round(StatConversion.GetAverageResistance(AttackerLevel, TargetLevel, TargetResistance, AttackerSpellPenetration) * 100.0f, 2).ToString() + "% average resistance \n";
            tipResist += "% Resisted     Occurance";

            float[] ResistTable = StatConversion.GetResistanceTable(AttackerLevel, TargetLevel, TargetResistance, AttackerSpellPenetration);
            for (count = 0; count < 10; count++)
            {
                if (ResistTable[count] > 0)
                {
                    tipResist += "\n" + Math.Round(count * 10.0f, 1) + " % resisted   " + Math.Round(ResistTable[count] * 100.0f, 2) + "%";
                }
            }

            return tipResist;
        }

        /// <summary>
        /// Returns the Minimum amount of Spell Damage will be resisted. (0.2 = Anything below 20% is always resisted)
        /// If this returns 0.0, that means you will always have a chance to take full damage from spells.
        /// If this returns 0.1, that means you will never take full damage from spells, and minumum you will take is 10% reduction.
        /// </summary>
        /// <param name="AttackerLevel">Level of Attacker</param>
        /// <param name="TargetLevel">Level of Target</param>
        /// <param name="TargetResistance">Target Resistance</param>
        /// <param name="AttackerSpellPenetration">Attacker Spell Penetration</param>
        /// <returns>The Minimum amount of Spell Damage will be resisted. (0.2 = Anything below 20% is always resisted)</returns>
        public static float GetMinimumResistance(int AttackerLevel, int TargetLevel,
            float TargetResistance, float AttackerSpellPenetration)
        {
            float[] ResistTable = GetResistanceTable(AttackerLevel, TargetLevel, TargetResistance, AttackerSpellPenetration);

            for (int x = 0; x < 11; x++)
                if (ResistTable[x] > 0f)
                    return 0.1f * x;
            return 0f;
        }

        // Initial function taken from the Juggernautior Module.
        // Then using table found on EJ:
        // http://elitistjerks.com/f31/t29453-combat_ratings_level_80_a/
        // creating updated Avoidance Chance w/ DR build in formula.
        /// <summary>
        /// 
        /// </summary>
        /// <param name="character">Character in question.</param>
        /// <param name="stats">Stats object... total stats of the character.</param>
        /// <param name="avoidanceType">What type of hit is the target doing on the character?</param>
        /// <param name="TargetLevel">Level of the target being fought</param>
        /// <returns>A % value where .50 == 50%</returns>
        public static float GetDRAvoidanceChance(Character character, Stats stats, HitResult avoidanceType, int TargetLevel) { return GetDRAvoidanceChance(character, stats, avoidanceType, (uint)TargetLevel); }
        public static float GetDRAvoidanceChance(Character character, Stats stats, HitResult avoidanceType, uint TargetLevel)
        {
            /*
            float defSkill = stats.Defense;
            // Let's make sure we don't run off the bottom w/ a negative defense rating.
            stats.DefenseRating = Math.Max(stats.DefenseRating, 0f);
            float defSkillMod = (GetDefenseFromRating(stats.DefenseRating, character.Class) * DEFENSE_RATING_AVOIDANCE_MULTIPLIER);
            float baseAvoid = (defSkill - (TargetLevel * 5)) * DEFENSE_RATING_AVOIDANCE_MULTIPLIER;
            float modifiedAvoid = defSkillMod;
            float finalAvoid = 0f; // I know it breaks my lack of redundancy rule, but it helps w/ readability.
            int iClass = (int)character.Class;
            */

            float baseAvoid     = 0.05f;
            float modifiedAvoid = 0.00f;
            float finalAvoid    = 0.00f;
            int iClass          = (int)character.Class;

            switch (avoidanceType)
            {
                case HitResult.Dodge:
                    if ((character.Class == CharacterClass.Juggernaut) || (character.Class == CharacterClass.Powertech) || (character.Class == CharacterClass.Assassin))
                        baseAvoid += (stats.Dodge * 100f);
                    else
                        baseAvoid += (stats.Dodge * 100f);
                    // Assuring we don't run off the bottom w/ negative dodge rating.
                    stats.DefenseRating = Math.Max(stats.DefenseRating, 0f);
                    modifiedAvoid = DRMath(CAP_DODGE_INV[iClass], DR_COEFFIENT[iClass], modifiedAvoid);
                    // Don't run off the bottom if we have negative dodge
                    finalAvoid = Math.Max(baseAvoid + modifiedAvoid, 0);
                    finalAvoid = Math.Min(finalAvoid, CAP_DODGE[iClass]);
                    break;
                case HitResult.Parry:
                    baseAvoid += stats.Parry * 100f;
                    // Assuring we don't run off the bottom w/ negative parry rating.
                    stats.DefenseRating = Math.Max(stats.DefenseRating, 0f);
                    modifiedAvoid += (GetDefenseFromRating(stats.DefenseRating) * 100f) / 2f;
                    modifiedAvoid = DRMath(CAP_PARRY_INV[iClass], DR_COEFFIENT[iClass], modifiedAvoid);
                    finalAvoid = Math.Max(baseAvoid + modifiedAvoid, 0);
                    finalAvoid = Math.Min(finalAvoid, CAP_PARRY[iClass]);
                    break;
                case HitResult.Miss:
                    // Base Miss rate according is 5%
                    // However, this can be talented up (e.g. Frigid Dreadplate, NE racial, etc.) 
                    baseAvoid += stats.Miss * 100f;
                    modifiedAvoid = DRMath( (1f/CAP_MISSED[iClass]), DR_COEFFIENT[iClass], modifiedAvoid );
                    // Factoring in the Miss Cap. 
                    modifiedAvoid = Math.Min(CAP_MISSED[iClass], modifiedAvoid);
                    finalAvoid = Math.Max(baseAvoid + modifiedAvoid, 0);
                    finalAvoid = Math.Min(finalAvoid, CAP_MISSED[iClass]);
                    break;
                case HitResult.Shield:
                    // Base Shield is 5%
                    baseAvoid += stats.Shield * 100f;
                    // Assuring we don't run off the bottom w/ negative block rating.
                    stats.ShieldRating = Math.Max(stats.ShieldRating, 0f);
                    modifiedAvoid += (GetShieldFromRating(stats.ShieldRating) * 100f);
                    finalAvoid = Math.Max(baseAvoid + modifiedAvoid, 0);
                    break;
                case HitResult.Crit:
                    // Resilience doesn't change crit chance anymore.
                    //modifiedAvoid -= (GetCritReductionFromResilience(stats.Resilience) * 100f);
                    finalAvoid = baseAvoid + modifiedAvoid;
                    break;
            }

            // Many of the base values are whole numbers, so need to get it back to decimal. 
            // May want to look at making this more consistant in the future.
            finalAvoid = finalAvoid / 100.0f;
            return finalAvoid;
        }
        /// <summary>
        /// StatPostDR =  1/(CAP_STAT_INV + COEF/StatPreDR)
        /// </summary>
        /// <param name="inv_cap">One of the CAP_STAT_INV values, appropriate for the class.</param>
        /// <param name="coefficient">One of the DR_COEF values, appropriate for the class.</param>
        /// <param name="valuePreDR">The value of the stat before DR are factored in.</param>
        /// <returns></returns>
        private static float DRMath(float inv_cap, float coefficient, float valuePreDR)
        {
            float DRValue = 0f;
            DRValue = 1f / (inv_cap + coefficient / valuePreDR);
            return DRValue;
        }
        #endregion
    }
}
